package com.bsi.login.oauth.handler.impl;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.internal.org.bouncycastle.util.encoders.Base64;
import com.bsi.login.oauth.config.OauthFieldProperties;
import com.bsi.login.oauth.entity.Oauth2ReqParamVo;
import com.bsi.login.oauth.entity.OauthVisitModeEnum;
import com.bsi.login.oauth.handler.IOauthRequestHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import io.micrometer.core.lang.Nullable;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/*
 * Author: liusongs
 * Date: 2019/10/18 11:16
 * Content: 默认的oauth请求service
 */
public class DefaultOauthRequestHandler implements IOauthRequestHandler {

    @Autowired
    private OauthFieldProperties oauthFieldProperties;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public <T> T tokenHandler(@NotNull OauthVisitModeEnum oauthVisitModeEnum, @Nullable HttpHeaders headers, @NotNull Oauth2ReqParamVo params, @NotNull Class<T> clazz) {
        // 根据传入的模式来修改配置
        OauthFieldProperties oauthField = this.oauthFieldProperties.selectTokenMode(oauthVisitModeEnum);

        // 准备调用服务
        ServiceInstance choose = loadBalancerClient.choose(oauthField.getServiceId());
        if (choose == null) { // oauth服务找不到
            throw new IllegalArgumentException("[DefaultOauthRequest.getToken] 通过此oauthSeviceId ->" + oauthField.getServiceId() + "未找到oauth服务...");
        }
        //自行封装请求头
        if (headers == null || headers.isEmpty()) {
            headers = new HttpHeaders();
        }
        if (headers.getContentType() == null) {
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        }
        if (CollectionUtils.isEmpty(headers.get(oauthField.getOauthHead()))) {
            headers.set(oauthField.getOauthHead(), getAuthorizationHeader(oauthField.getClientId(), oauthField.getClientSecret()));
        }
        // 拼接请求地址
        String tokenUri = new StringBuilder().append(choose.getUri()).append(oauthField.getTokenUriInfo()).toString(); // 拼接服务地址
        // 发送请求
        T body = restTemplate.exchange(tokenUri, HttpMethod.POST, new HttpEntity<MultiValueMap<String, Object>>(beanToMap(params), headers),
                (Class<T>) clazz).getBody();
        return body;
    }

    /**
     * 将对象转为map
     *
     * @param oauth2ReqVo
     * @return
     */
    private MultiValueMap<String, Object> beanToMap(Oauth2ReqParamVo oauth2ReqVo) {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
        Class<?> aClass = oauth2ReqVo.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        try {
            // 将基本属性转换为map
            for (Field field : declaredFields) {
                field.setAccessible(true);
                String name = field.getName();
                Object o = field.get(oauth2ReqVo);
                if (Objects.isNull(o)) {
                    continue;
                }
                if (o instanceof Map) {
                    continue;
                }
                map.add(field.getName(), o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 合并参数
        if (Objects.nonNull(oauth2ReqVo.getOtherParam()) && !oauth2ReqVo.getOtherParam().isEmpty()) {
            map.setAll(oauth2ReqVo.getOtherParam());
        }
        return map;
    }

    /**
     * 将clientId clinetSecret 转换成 Basic Base64(clientId:clinetSecret)的形式
     *
     * @param clientId
     * @param clientSecret
     * @return
     */
    public String getAuthorizationHeader(String clientId, String clientSecret) {
        if (Objects.isNull(clientId) || Objects.isNull(clientSecret)) {
            throw new IllegalArgumentException("clientId and clientSecret must be not null !");
        }
        String creds = String.format("%s:%s", clientId, clientSecret);
        try {
            return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("getAuthorizationHeader coverting fail !");
        }
    }
}
